package com.demo13;

import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.Symbol;
import com.github.unidbg.arm.HookStatus;
import com.github.unidbg.arm.backend.Unicorn2Factory;
import com.github.unidbg.arm.context.RegisterContext;
import com.github.unidbg.debugger.BreakPointCallback;
import com.github.unidbg.file.FileResult;
import com.github.unidbg.file.IOResolver;
import com.github.unidbg.file.linux.AndroidFileIO;
import com.github.unidbg.hook.HookContext;
import com.github.unidbg.hook.ReplaceCallback;
import com.github.unidbg.hook.xhook.IxHook;
import com.github.unidbg.linux.android.*;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.linux.file.ByteArrayFileIO;
import com.github.unidbg.linux.file.SimpleFileIO;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.memory.SvcMemory;
import com.github.unidbg.unix.UnixSyscallHandler;
import unicorn.ArmConst;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.UUID;

public class dewuNew extends AbstractJni implements IOResolver {
    @Override
    public FileResult resolve(Emulator emulator, String pathname, int oflags) {
        System.out.println("file open:"+pathname);
        if(pathname.equals("proc/sys/kernel/random/boot")){
            return FileResult.success(new ByteArrayFileIO(oflags, pathname, UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8)));
        }
        if(pathname.equals("/proc/cpuinfo")){
            return FileResult.success(new SimpleFileIO(oflags, new File("unidbg-android/src/test/resources/demo13/files/cpuinfo"), pathname));
        }
        if(pathname.equals("/proc/version")){
            return FileResult.success(new SimpleFileIO(oflags, new File("unidbg-android/src/test/resources/demo13/files/version"), pathname));
        }
        if(pathname.equals("/proc/stat")){
            String stat = "cpu  1209192 278928 2191380 5564335 69710 164500 158299 0 0 0\n" +
                    "cpu0 272080 47122 771336 5564279 69710 83363 88819 0 0 0\n" +
                    "cpu1 278487 47788 509493 7 0 26776 22452 0 0 0\n" +
                    "cpu2 306880 43295 389021 7 0 22909 20684 0 0 0\n" +
                    "cpu3 174831 20941 318552 5 0 25036 22103 0 0 0\n" +
                    "cpu4 58459 36080 51694 9 0 1919 1318 0 0 0\n" +
                    "cpu5 51073 31143 41167 9 0 1674 1224 0 0 0\n" +
                    "cpu6 38577 28601 34271 9 0 1461 897 0 0 0\n" +
                    "cpu7 28805 23958 75846 7 0 1358 799 0 0 0\n" +
                    "intr 155705479 0 0 0 0 17366157 0 2541200 151140 0 0 0 0 3160250 0 77 0 217647 15455 43218 3574258 0 2 0 0 0 1853 658 11 11 0 0 0 0 0 0 0 0 0 0 0 0 0 1845 0 136 0 0 0 0 0 0 637834 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 378 0 0 0 0 0 0 0 23 10518882 224481 20256 12683 779304 486 0 15 348 0 18 835077 1931888 740831 0 0 0 0 0 196172 382972 2904068 0 0 0 0 0 2858 0 0 0 0 0 0 0 0 0 0 0 0 111870 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 5 4 4 0 0 0 1395 0 1 0 2 0 2 0 2 1 0 0 2 79 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 41 0 0 2 12160 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 123625 11100 0 0 0 0 0 0 1 0 63 0 0 18 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1827 2399 0 120 379 1322 0 129 0 0 0 0 0 0 0 2 0 0 0 0 0 206 3 37 0 3 583 3003 11 0 0 0 0 0 0 0 0 1186 0 1259 94 21509 0 940 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 13 0 0 286 3 0 4 7 0 0 0 0 12 0 0 600507 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" +
                    "ctxt 303843631\n" +
                    "btime 1654535232\n" +
                    "processes 121585\n" +
                    "procs_running 5\n" +
                    "procs_blocked 0\n" +
                    "softirq 52763266 11220442 11237397 669917 1413834 3167347 0 6287292 9643900 0 9123137";
            return FileResult.success(new ByteArrayFileIO(oflags, pathname, stat.getBytes()));
        }

        return null;
    }

    private final AndroidEmulator emulator;
    private final VM vm;
    private final Module module;
    private final DvmClass dvmClass;

    dewuNew() {
        // 创建模拟器实例
        AndroidEmulatorBuilder builder = new AndroidEmulatorBuilder(false){
            @Override
            public AndroidEmulator build() {
                return new AndroidARMEmulator(processName,rootDir,backendFactories) {
                    @Override
                    protected UnixSyscallHandler<AndroidFileIO> createSyscallHandler(SvcMemory svcMemory) {
                        return new dewuSyscallHandler(svcMemory);
                    }
                };
            }
        };
        emulator = builder
                .setRootDir(new File("unidbg-android/src/test/resources/demo13/rootfs"))
                .addBackendFactory(new Unicorn2Factory(true))
                .build();

        // 模拟器的内存操作接口
        final Memory memory = emulator.getMemory();
        // 设置系统类库解析
        memory.setLibraryResolver(new AndroidResolver(23));
        vm = emulator.createDalvikVM(new File("unidbg-android/src/test/resources/demo13/4.9.8.apk"));
        vm.setJni(this);
        vm.setVerbose(true);
        SystemPropertyHook systemPropertyHook = new SystemPropertyHook(emulator);
        systemPropertyHook.setPropertyProvider(key -> {
            System.out.println("lilac systemkey:" + key);
            switch (key) {
                case "net.hostname":{
                    return "MIX2S-zhongeryayadeM";
                }
                case "ro.serialno":{
                    return "f8a995f5";
                }
                case "ro.boot.serialno":{
                    return "f8a995f5";
                }
                case "ro.product.brand":{
                    return "Xiaomi";
                }
                case "ro.product.manufacturer":{
                    return "Xiaomi";
                }
                case "ro.product.model":{
                    return "MIX 2S";
                }
                case "ro.product.cpu.abi":{
                    return "arm64-v8a";
                }
                case "ro.product.cpu.abilist":{
                    return "arm64-v8a,armeabi-v7a,armeabi";
                }
                case "ro.boot.vbmeta.digest":{
                    return null;
                }
                case "init.svc.droid4x":{
                    return null;
                }
            }
            return "";
        });
        memory.addHookListener(systemPropertyHook);

        emulator.getSyscallHandler().addIOResolver(this);
        emulator.getSyscallHandler().setEnableThreadDispatcher(true);
        emulator.getBackend().registerEmuCountHook(100000); // 设置执行多少条指令切换一次线程
        DalvikModule dm = vm.loadLibrary("szstone", true);

        module = dm.getModule();

        dm.callJNI_OnLoad(emulator);
        dvmClass = vm.resolveClass("com.shizhuang.stone.main.SzSdk");

        recordPopenByxHook();
        handleIoctl();
    }


    public void handleIoctl(){
        Symbol ioctlSymbol = module.findSymbolByName("ioctl");
        emulator.attach().addBreakPoint(ioctlSymbol.getAddress(), new BreakPointCallback() {
            @Override
            public boolean onHit(Emulator<?> emulator, long address) {
                // 顺利则返回 0
                RegisterContext registerContext = emulator.getContext();
                int requestId = registerContext.getIntByReg(ArmConst.UC_ARM_REG_R1);
                System.out.println("requestID:"+Integer.toHexString(requestId));
                // #define SIOCGIFHWADDR 0x8927 /* Get hardware address */
                if(requestId==0x8927){
                    emulator.getBackend().reg_write(ArmConst.UC_ARM_REG_R0, -1);
                    emulator.getBackend().reg_write(ArmConst.UC_ARM_REG_PC, registerContext.getLRPointer().peer);
                }
                return true;
            }
        });
    }

    public void recordPopenByxHook(){
        IxHook xHook = XHookImpl.getInstance(emulator);
        xHook.register("libszstone.so", "popen", new ReplaceCallback() {
            @Override
            public HookStatus onCall(Emulator<?> emulator, HookContext context, long originFunction) {
                RegisterContext registerContext = emulator.getContext();
                String command = registerContext.getPointerArg(0).getString(0);
                emulator.set("command", command);
                return HookStatus.RET(emulator, originFunction);
            }
        }, true);
        // 使其生效
        xHook.refresh();
    }

    public String call_fun() {
        String a1 = "awt0bapt/data/user/0/com.shizhuang.duapp/filesbav4.94.0bavn486bcndewubc";
        int a2 = 1;
        int a3 = 0;
        byte[] bytes = (byte[]) dvmClass.callStaticJniMethodObject(emulator, "lf(Ljava/lang/String;II)[B", a1, a2, a3).getValue();
        System.out.println(bytes.length);
        return new String(Base64.encode(bytes, 2));
    }

    /* s must be an even-length string. */
    public static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                    + Character.digit(s.charAt(i+1), 16));
        }
        return data;
    }

    public static void main(String[] args) {
        dewuNew dewuNew = new dewuNew();
        System.out.println(dewuNew.call_fun());
    }


    @Override
    public DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
        switch (signature){
            case "android/app/ActivityThread->currentActivityThread()Landroid/app/ActivityThread;":{
                return dvmClass.newObject(null);
            }
            case "android/provider/Settings$Secure->getString(Landroid/content/ContentResolver;Ljava/lang/String;)Ljava/lang/String;":{
                String name = varArg.getObjectArg(1).getValue().toString();
                switch (name){
                    case "android_id":{
                        return new StringObject(vm, "07916307a127cdf0");
                    }
                }
            }
        }
        return super.callStaticObjectMethod(vm, dvmClass, signature, varArg);
    }

    @Override
    public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
        switch (signature){
            case "android/app/ActivityThread->getApplication()Landroid/app/Application;":{
                return vm.resolveClass("android/app/Application").newObject(null);
            }
            case "android/app/Application->getPackageManager()Landroid/content/pm/PackageManager;":{
                return vm.resolveClass("android/content/pm/PackageManager").newObject(null);
            }
            case "android/app/Application->getPackageName()Ljava/lang/String;":{
                return new StringObject(vm, vm.getPackageName());
            }
            case "android/app/Application->getContentResolver()Landroid/content/ContentResolver;":{
                return vm.resolveClass("android/content/ContentResolver").newObject(null);
            }
        }
        return super.callObjectMethod(vm, dvmObject, signature, varArg);
    }


}
